package top.yangbuyi.ExceptionHandelr;


import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import top.yangbuyi.acpect.enums.BusinessStatus;
import top.yangbuyi.domain.OperLog;
import top.yangbuyi.domain.User;
import top.yangbuyi.service.impl.OperLogService;
import top.yangbuyi.utils.AddressUtils;
import top.yangbuyi.utils.IpUtils;
import top.yangbuyi.utils.ServletUtils;
import top.yangbuyi.utils.ShiroSecurityUtils;
import top.yangbuyi.vo.AjaxResult;

import java.util.*;


/**
 * @description: 杨不易网站:www.yangbuyi.top
 * @program: yangbuyiopenHis2020
 * @ClassName: GlobalExceptionHandler
 * @create: 2020-08-28 11:13
 * @author: yangbuyi
 * @since： JDK1.8
 * @GlobalExceptionHandler: 全局异常处理$
 **/

@Slf4j
//@Order(3)
@RestControllerAdvice
public class GlobalExceptionHandler {
	  @Autowired
	  private OperLogService operLogService;

	  @ExceptionHandler(value = myException.class)
	  public AjaxResult myException(myException e) {
			return AjaxResult.ex("服务器内部错误:{请联系管理员}" + e.getMessage());
	  }

	  @ExceptionHandler(value = Exception.class)
	  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	  public Map<String, Object> AllException(Exception e) {
			Map<String, Object> result = new HashMap<>();
			result.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
			result.put("msg", e.getMessage());
 			return result;
	  }

	  /**
	   * 当系统出现BindException这个异常时，会调用下面的方法
	   * 如果页面传入的参数格式不是json,并且为空 则触发
	   * 介绍：
	   * 当客户端请求访问某个接口时，触发400 参数错误 方法参数无效异常 时
	   * 我们进行捕获返回客户端看得懂的数据
	   * 校验错误：
	   * 比如： name: 字段不能为空........
	   *
	   * @param e
	   * @return
	   */
	  @ExceptionHandler(value = BindException.class)
	  public AjaxResult keyAndValueErrorHandler(BindException ex) {
			return getAjaxResult(ex.getBindingResult());
	  }


	  /**
	   * @功能描述: * 当系统出现MethodArgumentNotValidException这个异常时，会调用下面的方法
	   * * 如果页面传入的参数为JSON格式,并且为空 则触发
	   * * 介绍：
	   * * 当客户端请求访问某个接口时，触发400 参数错误 方法参数无效异常 时
	   * * 我们进行捕获返回客户端看得懂的数据
	   * * 校验错误：
	   * * 比如： name: 字段不能为空........
	   * @Author: TeouBle
	   * @Date: 2020/8/29 22:05
	   */
	  @ExceptionHandler(value = MethodArgumentNotValidException.class)
	  public AjaxResult jsonErrorHandler(MethodArgumentNotValidException ex) {
			return getAjaxResult(ex.getBindingResult());
	  }

	  /**
	   * 抽象方法
	   * 介绍：
	   * 当客户端请求访问某个接口时，触发400 参数错误 方法参数无效异常 时
	   * 我们进行捕获返回客户端看得懂的数据
	   * 校验错误：
	   * 比如： name: 字段不能为空........
	   *
	   * @param bindingResult
	   * @return
	   */
	  private AjaxResult getAjaxResult(BindingResult bindingResult) {
			List<Map<String, Object>> list = new ArrayList<>();
			// 获取异常当中的对象错误
			List<ObjectError> allErrors = bindingResult.getAllErrors();
			for (ObjectError allError : allErrors) {
				  Map<String, Object> map = new HashMap<>(2);
				  map.put("defaultMessage", allError.getDefaultMessage());
				  map.put("objectName", allError.getObjectName());
				  //注意，这里面拿到具体的某一个属性
				  FieldError fieldError = (FieldError) allError;
				  map.put("field", fieldError.getField());
				  list.add(map);
			}
			log.error("获取异常当中的对象:{}" + allErrors);

			// 获取当前的用户
			User loginUser = ShiroSecurityUtils.getCurrentUser();
			// *========数据库日志=========*//
			OperLog operLog = new OperLog();
			// 请求的地址
			String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
			operLog.setOperIp(ip);
			// 设置ip是什么请求
			String address = AddressUtils.getRealAddressByIP(ip);
			operLog.setOperLocation(address);
			operLog.setTitle("参数错误");
			// 判断错误异常
			// 返回参数
			operLog.setJsonResult("后端数据校验异常:{对应字段处理}" + JSON.toJSONString(list));

			// 设置请求地址
			operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
			if (loginUser != null) {
				  operLog.setOperName(loginUser.getUserName());
			}
			operLog.setOperParam(JSON.toJSONString(list));
			// 设置错误信息
			operLog.setStatus(String.valueOf(BusinessStatus.FAIL.ordinal()));
			operLog.setErrorMsg(StringUtils.substring("后端数据校验异常:{对应字段处理}" + JSON.toJSONString(list), 0, 20000));
			// 设置请求方式0
			operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
			operLog.setMethod("BindingResult--参数错误");
			//设置操作时间
			operLog.setOperTime(DateUtil.date(new Date()));
			// 保存数据库
			operLogService.insertOperLog(operLog);

			return AjaxResult.ex("后端数据校验异常:{对应字段处理}" + list);
	  }

}
